package com.exam.service.base;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.exam.bean.QueryResult;

@Transactional
public abstract class DaoSupport implements DAO {
	
	@PersistenceContext protected EntityManager em;
	
	
	public <T> void delete(Class<T> entityClass, Object entityId) {
		
		delete(entityClass,new Object[]{entityId});
	}
	
	
	public <T> void delete(Class<T> entityClass, Object[] entityIds) {
		
		for(Object id : entityIds){
			em.remove(em.getReference(entityClass, id));
		}	
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> T find(Class<T> entityClass, Object entiyId) {
		
		return em.find(entityClass, entiyId);	
	}
	
	
	@Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED)
	public <T> long getCount(Class<T> entityClass) {
		
		return (Long)em.createQuery("select count("+ getCountField(entityClass) +") from "+ getEntityName(entityClass)+ " o").getSingleResult();
	}

	
	public void save(Object entity) {
		
		em.persist(entity);
	}

	public void update(Object entity) {
		
		em.merge(entity);
	}
	
	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,int firstIndex
			,int maxResult,LinkedHashMap<String, String> orderby) {
		
		return getScrollData(entityClass,firstIndex,maxResult,null,null,orderby);
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,
			int firstIndex, int maxResult, String wherejpql,
			Object[] queryParams) {
		
		return getScrollData(entityClass,firstIndex,maxResult,wherejpql,queryParams,null);
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,
			int firstIndex, int maxResult) {
		
		return getScrollData(entityClass,firstIndex,maxResult,null,null,null);
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,
			LinkedHashMap<String, String> orderby) {
		
		return getScrollData(entityClass,-1,-1,null,null,orderby);
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,
			String wherejpql, Object[] queryParams,
			LinkedHashMap<String, String> orderby) {
		
		return getScrollData(entityClass,-1,-1,wherejpql,queryParams,orderby);
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,
			String wherejpql, Object[] queryParams) {
		
		return getScrollData(entityClass,-1,-1,wherejpql,queryParams,null);
	}

	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass) {
		
		return getScrollData(entityClass,-1,-1,null,null,null);
	}

	
	@SuppressWarnings("unchecked")
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,int firstIndex
			,int maxResult,String wherejpql,Object[] queryParams
			,LinkedHashMap<String, String> orderby) {
		
		QueryResult qr=new QueryResult<T>();
		String entityName=getEntityName(entityClass);
		
		Query query=em.createQuery("select o from "+entityName+" o "
				+(wherejpql==null?"":"where "+wherejpql)+buildOrderby(orderby));
		setQueryParams(query,queryParams);
		if(firstIndex!=-1&&maxResult!=-1){
			query.setFirstResult(firstIndex).setMaxResults(maxResult);
		}
		qr.setResultList(query.getResultList());
		
		query=em.createQuery("select count("+ getCountField(entityClass) +") from "+entityName+" o "
				+(wherejpql==null?"":"where "+wherejpql));
		setQueryParams(query,queryParams);
		qr.setTotalRecord((Long)query.getSingleResult());
		return qr;
	}

	
	/**
	 * set the parameter of query
	 * @param query 'the object of query'
	 * @param queryParams 'the parameter of query'
	 */
	protected void setQueryParams(Query query,Object[] queryParams){
		if(queryParams!=null&&queryParams.length>0){
			for(int i=0;i<queryParams.length;i++){
				query.setParameter(i+1, queryParams[i]);
			}
		}
	}
	
	
	/**
	 * build the jpql of 'order by'
	 * @param orderby 'the set of order field'
	 * @return
	 */
	protected String buildOrderby(LinkedHashMap<String, String> orderby){
		StringBuffer orderbyql=new StringBuffer("");
		if(orderby!=null&&orderby.size()>0){
			orderbyql.append(" order by ");
			for(String key : orderby.keySet()){
				orderbyql.append("o.").append(key).append(" ").append(orderby.get(key)).append(",");
			}
			orderbyql.deleteCharAt(orderbyql.length()-1);
		}
		return orderbyql.toString();
	}
	
	
	/**
	 * get the name of entity
	 * @param <T>
	 * @param entityClass 
	 * @return
	 */
	protected <T> String getEntityName(Class<T> entityClass){
		String entityName=entityClass.getSimpleName();
		Entity entity=entityClass.getAnnotation(Entity.class);
		if(entity.name()!=null&&!"".equals(entity.name())){
			entityName=entity.name();
		}
		return entityName;
	}
	
	protected <T> String getCountField(Class<T> clazz){
		String out = "o";
		try {
			PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
			for(PropertyDescriptor propertydesc : propertyDescriptors){
				Method method = propertydesc.getReadMethod();
				if(method!=null && method.isAnnotationPresent(EmbeddedId.class)){					
					PropertyDescriptor[] ps = Introspector.getBeanInfo(propertydesc.getPropertyType()).getPropertyDescriptors();
					out = "o."+ propertydesc.getName()+ "." + (!ps[1].getName().equals("class")? ps[1].getName(): ps[0].getName());
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
        return out;
	}
}
